"""
绩效分析

提供投资组合绩效分析、风险指标计算、基准比较等功能。
"""

from typing import Dict, List, Optional, Any, Tuple
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
from decimal import Decimal
import math

from .portfolio import Portfolio, PortfolioSnapshot


class PerformancePeriod(Enum):
    """绩效分析周期"""
    DAILY = "daily"
    WEEKLY = "weekly"
    MONTHLY = "monthly"
    QUARTERLY = "quarterly"
    YEARLY = "yearly"
    INCEPTION = "inception"  # 成立以来


class RiskMetric(Enum):
    """风险指标"""
    VOLATILITY = "volatility"  # 波动率
    VAR = "var"  # 风险价值
    CVAR = "cvar"  # 条件风险价值
    MAX_DRAWDOWN = "max_drawdown"  # 最大回撤
    SHARPE_RATIO = "sharpe_ratio"  # 夏普比率
    SORTINO_RATIO = "sortino_ratio"  # 索提诺比率
    CALMAR_RATIO = "calmar_ratio"  # 卡玛比率
    BETA = "beta"  # Beta系数
    ALPHA = "alpha"  # Alpha系数
    INFORMATION_RATIO = "information_ratio"  # 信息比率


@dataclass
class PerformanceMetrics:
    """绩效指标"""
    # 收益指标
    total_return: float = 0.0  # 总收益率
    annualized_return: float = 0.0  # 年化收益率
    cumulative_return: float = 0.0  # 累计收益率
    
    # 风险指标
    volatility: float = 0.0  # 波动率
    annualized_volatility: float = 0.0  # 年化波动率
    max_drawdown: float = 0.0  # 最大回撤
    var_95: float = 0.0  # 95% VaR
    cvar_95: float = 0.0  # 95% CVaR
    
    # 风险调整收益指标
    sharpe_ratio: float = 0.0  # 夏普比率
    sortino_ratio: float = 0.0  # 索提诺比率
    calmar_ratio: float = 0.0  # 卡玛比率
    
    # 相对指标（与基准比较）
    beta: float = 0.0  # Beta系数
    alpha: float = 0.0  # Alpha系数
    information_ratio: float = 0.0  # 信息比率
    tracking_error: float = 0.0  # 跟踪误差
    
    # 其他指标
    win_rate: float = 0.0  # 胜率
    profit_loss_ratio: float = 0.0  # 盈亏比
    max_consecutive_wins: int = 0  # 最大连续盈利次数
    max_consecutive_losses: int = 0  # 最大连续亏损次数
    
    # 计算参数
    period: PerformancePeriod = PerformancePeriod.INCEPTION
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None
    risk_free_rate: float = 0.03  # 无风险利率
    
    created_time: datetime = field(default_factory=datetime.now)


@dataclass
class DrawdownPeriod:
    """回撤期间"""
    start_date: datetime
    end_date: Optional[datetime]
    peak_value: Decimal
    trough_value: Decimal
    drawdown: float  # 回撤幅度
    duration: int  # 持续天数
    recovered: bool = False  # 是否已恢复


class PerformanceAnalyzer:
    """
    绩效分析器
    
    分析投资组合的绩效表现，计算各种风险收益指标。
    """
    
    def __init__(self, risk_free_rate: float = 0.03):
        """初始化绩效分析器"""
        self.risk_free_rate = risk_free_rate
        self.benchmark_data: Dict[datetime, Decimal] = {}  # 基准数据
    
    def set_benchmark_data(self, benchmark_data: Dict[datetime, Decimal]) -> None:
        """设置基准数据"""
        self.benchmark_data = benchmark_data
    
    def calculate_returns(self, snapshots: List[PortfolioSnapshot]) -> List[float]:
        """计算收益率序列"""
        if len(snapshots) < 2:
            return []
        
        returns = []
        for i in range(1, len(snapshots)):
            prev_value = snapshots[i-1].total_value
            curr_value = snapshots[i].total_value
            
            if prev_value > 0:
                return_rate = float((curr_value - prev_value) / prev_value)
                returns.append(return_rate)
            else:
                returns.append(0.0)
        
        return returns
    
    def calculate_cumulative_returns(self, returns: List[float]) -> List[float]:
        """计算累计收益率序列"""
        cumulative_returns = []
        cumulative = 1.0
        
        for ret in returns:
            cumulative *= (1 + ret)
            cumulative_returns.append(cumulative - 1)
        
        return cumulative_returns
    
    def calculate_volatility(self, returns: List[float], annualized: bool = False) -> float:
        """计算波动率"""
        if len(returns) < 2:
            return 0.0
        
        mean_return = sum(returns) / len(returns)
        variance = sum((r - mean_return) ** 2 for r in returns) / (len(returns) - 1)
        volatility = math.sqrt(variance)
        
        if annualized:
            # 假设每年252个交易日
            volatility *= math.sqrt(252)
        
        return volatility
    
    def calculate_max_drawdown(self, snapshots: List[PortfolioSnapshot]) -> Tuple[float, List[DrawdownPeriod]]:
        """计算最大回撤和回撤期间"""
        if len(snapshots) < 2:
            return 0.0, []
        
        max_drawdown = 0.0
        drawdown_periods = []
        
        peak_value = snapshots[0].total_value
        peak_date = snapshots[0].timestamp
        current_drawdown_start = None
        
        for snapshot in snapshots[1:]:
            current_value = snapshot.total_value
            
            if current_value > peak_value:
                # 新高点，结束当前回撤期
                if current_drawdown_start:
                    drawdown = float((peak_value - current_value) / peak_value)
                    duration = (snapshot.timestamp - current_drawdown_start).days
                    
                    drawdown_periods.append(DrawdownPeriod(
                        start_date=current_drawdown_start,
                        end_date=snapshot.timestamp,
                        peak_value=peak_value,
                        trough_value=current_value,
                        drawdown=drawdown,
                        duration=duration,
                        recovered=True
                    ))
                    current_drawdown_start = None
                
                peak_value = current_value
                peak_date = snapshot.timestamp
            else:
                # 回撤中
                if current_drawdown_start is None:
                    current_drawdown_start = peak_date
                
                drawdown = float((peak_value - current_value) / peak_value)
                if drawdown > max_drawdown:
                    max_drawdown = drawdown
        
        # 处理未结束的回撤期
        if current_drawdown_start and len(snapshots) > 0:
            last_snapshot = snapshots[-1]
            drawdown = float((peak_value - last_snapshot.total_value) / peak_value)
            duration = (last_snapshot.timestamp - current_drawdown_start).days
            
            drawdown_periods.append(DrawdownPeriod(
                start_date=current_drawdown_start,
                end_date=None,
                peak_value=peak_value,
                trough_value=last_snapshot.total_value,
                drawdown=drawdown,
                duration=duration,
                recovered=False
            ))
        
        return max_drawdown, drawdown_periods
    
    def calculate_var(self, returns: List[float], confidence_level: float = 0.95) -> float:
        """计算风险价值(VaR)"""
        if len(returns) < 2:
            return 0.0
        
        sorted_returns = sorted(returns)
        index = int((1 - confidence_level) * len(sorted_returns))
        
        if index >= len(sorted_returns):
            index = len(sorted_returns) - 1
        
        return abs(sorted_returns[index])
    
    def calculate_cvar(self, returns: List[float], confidence_level: float = 0.95) -> float:
        """计算条件风险价值(CVaR)"""
        if len(returns) < 2:
            return 0.0
        
        var = self.calculate_var(returns, confidence_level)
        tail_returns = [r for r in returns if r <= -var]
        
        if not tail_returns:
            return var
        
        return abs(sum(tail_returns) / len(tail_returns))
    
    def calculate_sharpe_ratio(self, returns: List[float], risk_free_rate: Optional[float] = None) -> float:
        """计算夏普比率"""
        if len(returns) < 2:
            return 0.0
        
        if risk_free_rate is None:
            risk_free_rate = self.risk_free_rate
        
        mean_return = sum(returns) / len(returns)
        volatility = self.calculate_volatility(returns)
        
        if volatility == 0:
            return 0.0
        
        # 年化处理
        annualized_return = mean_return * 252
        annualized_volatility = volatility * math.sqrt(252)
        
        return (annualized_return - risk_free_rate) / annualized_volatility
    
    def calculate_sortino_ratio(self, returns: List[float], risk_free_rate: Optional[float] = None) -> float:
        """计算索提诺比率"""
        if len(returns) < 2:
            return 0.0
        
        if risk_free_rate is None:
            risk_free_rate = self.risk_free_rate
        
        mean_return = sum(returns) / len(returns)
        
        # 只考虑负收益的波动率
        negative_returns = [r for r in returns if r < 0]
        if not negative_returns:
            return float('inf') if mean_return > risk_free_rate / 252 else 0.0
        
        downside_variance = sum(r ** 2 for r in negative_returns) / len(returns)
        downside_volatility = math.sqrt(downside_variance)
        
        if downside_volatility == 0:
            return 0.0
        
        # 年化处理
        annualized_return = mean_return * 252
        annualized_downside_volatility = downside_volatility * math.sqrt(252)
        
        return (annualized_return - risk_free_rate) / annualized_downside_volatility
    
    def calculate_calmar_ratio(self, returns: List[float], max_drawdown: float) -> float:
        """计算卡玛比率"""
        if len(returns) < 2 or max_drawdown == 0:
            return 0.0
        
        mean_return = sum(returns) / len(returns)
        annualized_return = mean_return * 252
        
        return annualized_return / max_drawdown
    
    def calculate_beta_alpha(
        self,
        portfolio_returns: List[float],
        benchmark_returns: List[float]
    ) -> Tuple[float, float]:
        """计算Beta和Alpha系数"""
        if len(portfolio_returns) != len(benchmark_returns) or len(portfolio_returns) < 2:
            return 0.0, 0.0
        
        # 计算协方差和方差
        portfolio_mean = sum(portfolio_returns) / len(portfolio_returns)
        benchmark_mean = sum(benchmark_returns) / len(benchmark_returns)
        
        covariance = sum((p - portfolio_mean) * (b - benchmark_mean) 
                        for p, b in zip(portfolio_returns, benchmark_returns)) / (len(portfolio_returns) - 1)
        
        benchmark_variance = sum((b - benchmark_mean) ** 2 
                               for b in benchmark_returns) / (len(benchmark_returns) - 1)
        
        if benchmark_variance == 0:
            return 0.0, 0.0
        
        beta = covariance / benchmark_variance
        alpha = (portfolio_mean - self.risk_free_rate / 252) - beta * (benchmark_mean - self.risk_free_rate / 252)
        
        # 年化Alpha
        alpha *= 252
        
        return beta, alpha
    
    def calculate_information_ratio(
        self,
        portfolio_returns: List[float],
        benchmark_returns: List[float]
    ) -> Tuple[float, float]:
        """计算信息比率和跟踪误差"""
        if len(portfolio_returns) != len(benchmark_returns) or len(portfolio_returns) < 2:
            return 0.0, 0.0
        
        # 计算超额收益
        excess_returns = [p - b for p, b in zip(portfolio_returns, benchmark_returns)]
        
        mean_excess_return = sum(excess_returns) / len(excess_returns)
        tracking_error = self.calculate_volatility(excess_returns, annualized=True)
        
        if tracking_error == 0:
            return 0.0, 0.0
        
        information_ratio = (mean_excess_return * 252) / tracking_error
        
        return information_ratio, tracking_error
    
    def calculate_win_rate_and_profit_loss_ratio(self, returns: List[float]) -> Tuple[float, float]:
        """计算胜率和盈亏比"""
        if not returns:
            return 0.0, 0.0
        
        positive_returns = [r for r in returns if r > 0]
        negative_returns = [r for r in returns if r < 0]
        
        win_rate = len(positive_returns) / len(returns)
        
        if not positive_returns or not negative_returns:
            profit_loss_ratio = 0.0
        else:
            avg_profit = sum(positive_returns) / len(positive_returns)
            avg_loss = abs(sum(negative_returns) / len(negative_returns))
            profit_loss_ratio = avg_profit / avg_loss if avg_loss > 0 else 0.0
        
        return win_rate, profit_loss_ratio
    
    def calculate_consecutive_periods(self, returns: List[float]) -> Tuple[int, int]:
        """计算最大连续盈利和亏损次数"""
        if not returns:
            return 0, 0
        
        max_consecutive_wins = 0
        max_consecutive_losses = 0
        current_consecutive_wins = 0
        current_consecutive_losses = 0
        
        for ret in returns:
            if ret > 0:
                current_consecutive_wins += 1
                current_consecutive_losses = 0
                max_consecutive_wins = max(max_consecutive_wins, current_consecutive_wins)
            elif ret < 0:
                current_consecutive_losses += 1
                current_consecutive_wins = 0
                max_consecutive_losses = max(max_consecutive_losses, current_consecutive_losses)
            else:
                current_consecutive_wins = 0
                current_consecutive_losses = 0
        
        return max_consecutive_wins, max_consecutive_losses
    
    def analyze_performance(
        self,
        portfolio: Portfolio,
        period: PerformancePeriod = PerformancePeriod.INCEPTION,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None
    ) -> PerformanceMetrics:
        """分析投资组合绩效"""
        snapshots = portfolio.snapshots
        
        # 过滤时间范围
        if start_date or end_date:
            filtered_snapshots = []
            for snapshot in snapshots:
                if start_date and snapshot.timestamp < start_date:
                    continue
                if end_date and snapshot.timestamp > end_date:
                    continue
                filtered_snapshots.append(snapshot)
            snapshots = filtered_snapshots
        
        if len(snapshots) < 2:
            return PerformanceMetrics(
                period=period,
                start_date=start_date,
                end_date=end_date,
                risk_free_rate=self.risk_free_rate
            )
        
        # 计算收益率序列
        returns = self.calculate_returns(snapshots)
        
        if not returns:
            return PerformanceMetrics(
                period=period,
                start_date=start_date,
                end_date=end_date,
                risk_free_rate=self.risk_free_rate
            )
        
        # 计算基本指标
        total_return = float((snapshots[-1].total_value - snapshots[0].total_value) / snapshots[0].total_value)
        
        # 计算年化收益率
        days = (snapshots[-1].timestamp - snapshots[0].timestamp).days
        if days > 0:
            annualized_return = (1 + total_return) ** (365.25 / days) - 1
        else:
            annualized_return = 0.0
        
        cumulative_return = total_return
        
        # 计算风险指标
        volatility = self.calculate_volatility(returns)
        annualized_volatility = self.calculate_volatility(returns, annualized=True)
        max_drawdown, _ = self.calculate_max_drawdown(snapshots)
        var_95 = self.calculate_var(returns, 0.95)
        cvar_95 = self.calculate_cvar(returns, 0.95)
        
        # 计算风险调整收益指标
        sharpe_ratio = self.calculate_sharpe_ratio(returns)
        sortino_ratio = self.calculate_sortino_ratio(returns)
        calmar_ratio = self.calculate_calmar_ratio(returns, max_drawdown)
        
        # 计算其他指标
        win_rate, profit_loss_ratio = self.calculate_win_rate_and_profit_loss_ratio(returns)
        max_consecutive_wins, max_consecutive_losses = self.calculate_consecutive_periods(returns)
        
        # 如果有基准数据，计算相对指标
        beta = 0.0
        alpha = 0.0
        information_ratio = 0.0
        tracking_error = 0.0
        
        if self.benchmark_data:
            # 获取对应时间的基准收益率
            benchmark_returns = []
            for i in range(1, len(snapshots)):
                snapshot_date = snapshots[i].timestamp.date()
                prev_date = snapshots[i-1].timestamp.date()
                
                # 查找最接近的基准数据
                benchmark_value = None
                prev_benchmark_value = None
                
                for date, value in self.benchmark_data.items():
                    if date.date() == snapshot_date:
                        benchmark_value = value
                    if date.date() == prev_date:
                        prev_benchmark_value = value
                
                if benchmark_value and prev_benchmark_value and prev_benchmark_value > 0:
                    benchmark_return = float((benchmark_value - prev_benchmark_value) / prev_benchmark_value)
                    benchmark_returns.append(benchmark_return)
                else:
                    benchmark_returns.append(0.0)
            
            if len(benchmark_returns) == len(returns):
                beta, alpha = self.calculate_beta_alpha(returns, benchmark_returns)
                information_ratio, tracking_error = self.calculate_information_ratio(returns, benchmark_returns)
        
        return PerformanceMetrics(
            total_return=total_return,
            annualized_return=annualized_return,
            cumulative_return=cumulative_return,
            volatility=volatility,
            annualized_volatility=annualized_volatility,
            max_drawdown=max_drawdown,
            var_95=var_95,
            cvar_95=cvar_95,
            sharpe_ratio=sharpe_ratio,
            sortino_ratio=sortino_ratio,
            calmar_ratio=calmar_ratio,
            beta=beta,
            alpha=alpha,
            information_ratio=information_ratio,
            tracking_error=tracking_error,
            win_rate=win_rate,
            profit_loss_ratio=profit_loss_ratio,
            max_consecutive_wins=max_consecutive_wins,
            max_consecutive_losses=max_consecutive_losses,
            period=period,
            start_date=start_date,
            end_date=end_date,
            risk_free_rate=self.risk_free_rate
        )
    
    def compare_portfolios(
        self,
        portfolios: Dict[str, Portfolio],
        period: PerformancePeriod = PerformancePeriod.INCEPTION
    ) -> Dict[str, PerformanceMetrics]:
        """比较多个投资组合的绩效"""
        results = {}
        
        for name, portfolio in portfolios.items():
            try:
                metrics = self.analyze_performance(portfolio, period)
                results[name] = metrics
            except Exception as e:
                print(f"分析组合 {name} 绩效失败: {e}")
        
        return results
    
    def generate_performance_report(self, metrics: PerformanceMetrics) -> Dict[str, Any]:
        """生成绩效报告"""
        return {
            'summary': {
                'total_return': f"{metrics.total_return:.2%}",
                'annualized_return': f"{metrics.annualized_return:.2%}",
                'volatility': f"{metrics.annualized_volatility:.2%}",
                'max_drawdown': f"{metrics.max_drawdown:.2%}",
                'sharpe_ratio': f"{metrics.sharpe_ratio:.3f}"
            },
            'return_metrics': {
                'total_return': metrics.total_return,
                'annualized_return': metrics.annualized_return,
                'cumulative_return': metrics.cumulative_return
            },
            'risk_metrics': {
                'volatility': metrics.volatility,
                'annualized_volatility': metrics.annualized_volatility,
                'max_drawdown': metrics.max_drawdown,
                'var_95': metrics.var_95,
                'cvar_95': metrics.cvar_95
            },
            'risk_adjusted_metrics': {
                'sharpe_ratio': metrics.sharpe_ratio,
                'sortino_ratio': metrics.sortino_ratio,
                'calmar_ratio': metrics.calmar_ratio
            },
            'relative_metrics': {
                'beta': metrics.beta,
                'alpha': metrics.alpha,
                'information_ratio': metrics.information_ratio,
                'tracking_error': metrics.tracking_error
            },
            'trading_metrics': {
                'win_rate': metrics.win_rate,
                'profit_loss_ratio': metrics.profit_loss_ratio,
                'max_consecutive_wins': metrics.max_consecutive_wins,
                'max_consecutive_losses': metrics.max_consecutive_losses
            },
            'analysis_parameters': {
                'period': metrics.period.value,
                'start_date': metrics.start_date.isoformat() if metrics.start_date else None,
                'end_date': metrics.end_date.isoformat() if metrics.end_date else None,
                'risk_free_rate': metrics.risk_free_rate,
                'created_time': metrics.created_time.isoformat()
            }
        }